package com.xyxeshang.es.music.backend.util;

import com.alibaba.fastjson.JSON;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xyx-Eshang
 */
@SuppressWarnings("all")
public class HttpClientUtil {

	private static RequestConfig requestConfig;

	@PostConstruct
	public void beforeInit() {
		// 配置信息
		HttpClientUtil.requestConfig = RequestConfig.custom()
				// 设置连接超时时间(单位毫秒)
				.setConnectTimeout(5000)
				// 设置请求超时时间(单位毫秒)
				.setConnectionRequestTimeout(5000)
				// socket读写超时时间(单位毫秒)
				.setSocketTimeout(5000)
				// 设置是否允许重定向(默认为true)
				.setRedirectsEnabled(true).build();
	}

	/**
	 * 底层函数：创建httpGet
	 *
	 * @param url
	 * @param headerMap
	 * @param paramMap
	 * @return
	 */
	public static HttpGet baseCreateHttpGet(Map<String, String> headerMap, String url, Map<String, Object> paramMap) throws UnsupportedEncodingException {
		//1. 设置param参数
		StringBuilder sb = new StringBuilder();
		int paramCount = 0;
		if (paramMap != null) {
			paramCount = paramMap.size();
		}
		// 至少有一个参数时，循环拼接
		if (paramCount != 0) {
			// 先拼接"?"
			sb.append("?");
			// 遍历Key和value
			int pointer = 0;
			for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
				String key = URLEncoder.encode(entry.getKey(), "utf-8");
				String value = URLEncoder.encode("" + entry.getValue(), "utf-8");
				//拼接数据
				sb.append(key).append("=").append(value);
				if (pointer != paramCount - 1) {
					sb.append("&");
					pointer++;
				}
			}
		}
		//2. 创建GET请求
		HttpGet httpGet = new HttpGet(url + sb);
		httpGet.setConfig(requestConfig);

		//3. 设置请求头
		if (headerMap != null && headerMap.size() != 0) {
			for (Map.Entry<String, String> entry : headerMap.entrySet()) {
				// 调用setHeader：无则添加，有则覆盖
				httpGet.setHeader(entry.getKey(), entry.getValue());
			}
		}
		return httpGet;
	}

	/**
	 * 底层函数：创建httpPost
	 *
	 * @param url
	 * @param headerMap
	 * @param paramMap
	 * @param body
	 * @return
	 */
	private static HttpPost baseCreateHttpPost(Map<String, String> headerMap, String url, Map<String, Object> paramMap, Object body) throws UnsupportedEncodingException {
		//1. 处理param参数
		StringBuilder sb = new StringBuilder();
		int paramCount = 0;
		if (paramMap != null) {
			paramCount = paramMap.size();
		}
		// 至少有一个参数时，循环拼接
		if (paramCount != 0) {
			// 先拼接"?"
			sb.append("?");
			// 遍历Key和value
			int pointer = 0;
			for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
				String key = URLEncoder.encode(entry.getKey(), "utf-8");
				String value = URLEncoder.encode("" + entry.getValue(), "utf-8");
				//拼接数据
				sb.append(key).append("=").append(value);
				if (pointer != paramCount - 1) {
					sb.append("&");
					pointer++;
				}
			}
		}

		//2. 处理body参数
		StringEntity stringEntity = null;
		if (body != null) {
			//3-1. 将value值转换成JSON
			String jsonValue = JSON.toJSONString(body);
			//3-2. 以UTF-8编码将JSON值转换成stringEntity
			stringEntity = new StringEntity(jsonValue, "UTF-8");
		}

		//3. 创建POST请求
		//3-1. 加入param参数
		HttpPost httpPost = new HttpPost(url + sb.toString());
		httpPost.setConfig(requestConfig);
		if (body != null) {
			//3-2. 加入body参数
			httpPost.setEntity(stringEntity);
			//3-3. 将请求头设置为json模式
			httpPost.setHeader("Content-Type", "application/json;charset=utf8");
		}

		//4. 设置请求头
		if (headerMap != null && headerMap.size() != 0) {
			for (Map.Entry<String, String> entry : headerMap.entrySet()) {
				// 调用setHeader：无则添加，有则覆盖
				httpPost.setHeader(entry.getKey(), entry.getValue());
			}
		}
		return httpPost;
	}

	/**
	 * 底层函数：创建携带文件的httpPost请求
	 *
	 * @param url
	 * @param headerMap
	 * @param paramMap
	 * @param fileMap
	 * @return
	 * @throws IOException
	 */
	private static HttpPost baseCreateHttpPostWithFile(Map<String, String> headerMap, String url, Map<String, Object> paramMap, Map<String, MultipartFile> fileMap) throws IOException {
		//1. 处理param参数
		StringBuilder sb = new StringBuilder();
		int paramCount = 0;
		if (paramMap != null) {
			paramCount = paramMap.size();
		}
		// 至少有一个参数时，循环拼接
		if (paramCount != 0) {
			// 先拼接"?"
			sb.append("?");
			// 遍历Key和value
			int pointer = 0;
			for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
				String key = URLEncoder.encode(entry.getKey(), "utf-8");
				String value = URLEncoder.encode("" + entry.getValue(), "utf-8");
				//拼接数据
				sb.append(key).append("=").append(value);
				if (pointer != paramCount - 1) {
					sb.append("&");
					pointer++;
				}
			}
		}

		//2. 处理文件
		MultipartEntityBuilder builder = MultipartEntityBuilder.create();
		HttpEntity httpEntity = null;
		//2-1. 遍历map的key和value，获取所有文件
		if (fileMap != null) {
			for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
				String key = entry.getKey();
				MultipartFile value = entry.getValue();
				//2-1. 文件加入到参数当中
				builder.addBinaryBody("file", value.getInputStream(), ContentType.APPLICATION_OCTET_STREAM, key);
				//2-2. 构建MultipartEntityBuilder，并赋值给HttpEntity
				httpEntity = builder.build();
			}
		}

		//3. 创建POST请求
		//3-1. 加入param参数
		HttpPost httpPost = new HttpPost(url + sb.toString());
		httpPost.setConfig(requestConfig);
		//3-2. 加入文件参数
		if (fileMap != null) {
			httpPost.setEntity(httpEntity);
		}

		//4. 设置请求头
		if (headerMap != null && headerMap.size() != 0) {
			for (Map.Entry<String, String> entry : headerMap.entrySet()) {
				// 调用setHeader：无则添加，有则覆盖
				httpPost.setHeader(entry.getKey(), entry.getValue());
			}
		}
		return httpPost;
	}

	/**
	 * 底层函数：发送请求
	 *
	 * @param httpRequestBase
	 * @return
	 * @throws IOException
	 */
	private static Map<String, Object> baseSendRequest(HttpRequestBase httpRequestBase) throws IOException {
		try {
			//1. 获得Http客户端
			// 创建一个HttpClient
			CloseableHttpClient httpClient = HttpClientBuilder.create().build();
			//2. 声明响应模型
			CloseableHttpResponse response = null;
			//3. 发送请求
			try {
				//3-1. 由客户端执行(发送)请求
				response = httpClient.execute(httpRequestBase);
				//3-2. 从响应模型中获取响应实体
				HttpEntity responseEntity = response.getEntity();
				Map<String, Object> responseMap = new HashMap<>();
				//3-3. 获取响应头
				Header[] headerArray = response.getAllHeaders();
				Map<String, Object> headerMap = new HashMap<>();
				for (Header header : headerArray) {
					headerMap.put(header.getName(), header.getValue());
				}
				//3-4. 获取响应体，以UTF-8编码解析成JSON
				String body = EntityUtils.toString(responseEntity, "utf-8");
				//3-5. 封装，返回
				responseMap.put("headers", headerMap);
				responseMap.put("body", body);

				return responseMap;
			} catch (Exception e) {
				throw e;
			} finally {
				try {
					// 释放资源
					if (httpClient != null) {
						httpClient.close();
					}
					if (response != null) {
						response.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} catch (SocketException e) {
			// Permission denied，此时需要再来一次
			return baseSendRequest(httpRequestBase);
		}
	}


	/**
	 * 01. 无参GET请求
	 *
	 * @param url
	 * @return
	 */
	public static Map<String, Object> get(String url) throws IOException {
		//1. 创建httpGet
		HttpGet get = baseCreateHttpGet(null, url, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(get);
	}

	/**
	 * 02. 携带请求头的无参GET请求
	 *
	 * @param url
	 * @param headerMap
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> get(Map<String, String> headerMap, String url) throws IOException {
		//1. 创建httpGet
		HttpGet get = baseCreateHttpGet(headerMap, url, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(get);
	}

	/**
	 * 03. 有参GET请求
	 *
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> getWithParams(String url, Map<String, Object> paramMap) throws IOException {
		//1. 创建httpGet
		HttpGet get = baseCreateHttpGet(null, url, paramMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(get);
	}

	/**
	 * 04. 携带请求头的有参GET请求
	 *
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> getWithParams(Map<String, String> headerMap, String url, Map<String, Object> paramMap) throws IOException {
		//1. 创建httpGet
		HttpGet get = baseCreateHttpGet(headerMap, url, paramMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(get);
	}

	/**
	 * 05. 无参POST请求
	 *
	 * @param url
	 * @return
	 */
	public static Map<String, Object> post(String url) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(null, url, null, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 06. 携带请求头的无参POST请求
	 *
	 * @param url
	 * @return
	 */
	public static Map<String, Object> post(Map<String, String> headerMap, String url) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(headerMap, url, null, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 07. 参数在params的POST请求
	 *
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> postWithParams(String url, Map<String, Object> paramMap) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(null, url, paramMap, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

	/**
	 * 08. 携带请求头的参数在params的POST请求
	 *
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> postWithParams(Map<String, String> headerMap, String url, Map<String, Object> paramMap) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(headerMap, url, paramMap, null);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 09. 参数在body的POST请求
	 *
	 * @param url
	 * @return
	 */
	public static Map<String, Object> postWithBody(String url, Object body) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(null, url, null, body);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 10. 携带请求头的参数在body的POST请求
	 *
	 * @param url
	 * @return
	 */
	public static Map<String, Object> postWithBody(Map<String, String> headerMap, String url, Object body) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(headerMap, url, null, body);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 11. 参数同时在param和body中的POST请求
	 *
	 * @param url
	 * @param paramMap
	 * @param body
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> postWithMix(String url, Map<String, Object> paramMap, Object body) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(null, url, paramMap, body);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

	/**
	 * 12. 携带请求头的参数同时在param和body中的POST请求
	 *
	 * @param url
	 * @param paramMap
	 * @param body
	 * @return
	 * @throws IOException
	 */
	public static Map<String, Object> postWithMix(Map<String, String> headerMap, String url, Map<String, Object> paramMap, Object body) throws IOException {
		//1. 创建httpPost
		HttpPost post = baseCreateHttpPost(headerMap, url, paramMap, body);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

	/**
	 * 13. 无参的文件上传POST请求
	 *
	 * @param fileMap
	 * @return
	 */
	public static Map<String, Object> postFile(String url, Map<String, MultipartFile> fileMap) throws IOException {
		//1. 创建带文件的httpPost
		HttpPost post = baseCreateHttpPostWithFile(null, url, null, fileMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

	/**
	 * 14. 携带请求头的无参的文件上传POST请求
	 *
	 * @param fileMap
	 * @return
	 */
	public static Map<String, Object> postFile(Map<String, String> headerMap, String url, Map<String, MultipartFile> fileMap) throws IOException {
		//1. 创建带文件的httpPost
		HttpPost post = baseCreateHttpPostWithFile(headerMap, url, null, fileMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

	/**
	 * 15. 参数在param的文件上传POST请求
	 *
	 * @param fileMap
	 * @return
	 */
	public static Map<String, Object> postFileWithParams(String url, Map<String, Object> paramMap, Map<String, MultipartFile> fileMap) throws IOException {
		//1. 创建带文件的httpPost
		HttpPost post = baseCreateHttpPostWithFile(null, url, paramMap, fileMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}


	/**
	 * 16. 携带请求头的参数在param的文件上传POST请求
	 *
	 * @param fileMap
	 * @return
	 */
	public static Map<String, Object> postFileWithParams(Map<String, String> headerMap, String url, Map<String, Object> paramMap, Map<String, MultipartFile> fileMap) throws IOException {
		//1. 创建带文件的httpPost
		HttpPost post = baseCreateHttpPostWithFile(headerMap, url, paramMap, fileMap);
		//2. 发送请求，返回响应结果
		return baseSendRequest(post);
	}

}
